//**************************************************************
//		AHDataSource.m
//			AppHider
//
//	Copyright (c) 2008 Metta Media
//
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//	THE SOFTWARE.
//**************************************************************
#import "AHDataSource.h"

#define MyDragType @"MyDragType"
#define NodePboardType @"NodePboardType"

@implementation AHDataSource

- (id) init
{
	self = [super init];
	if (self != nil) {
		appOrder = [[NSMutableArray alloc] init];
		appArray = [[NSMutableArray alloc] init];
		AHPrefs = [[AHPreferences alloc] init];
		supportFolder = [MABSupportFolder sharedController];
		NSLog(@"support folder is %@", [supportFolder supportFolder]);
		[self setPrefs];
		[self readApplicationPlists];
		rootNode = [[ApplicationMenu alloc] init];
		editNode = NO;
		isUpdating = NO;
		notificationCenter = [NSNotificationCenter defaultCenter];
		[[[appIcon menu] itemWithTitle:@"Reset icon"] setAction:nil];
		NSLog(@"the origin is x=%d, y=%d", [sidebarTab frame].origin.x,[sidebarTab frame].origin.y);
		NSLog(@"the size is width=%d, height=%d", [sidebarTab frame].size.width,[sidebarTab frame].size.height);
	}
	return self;
}

- (void) awakeFromNib
{
	[appTable registerForDraggedTypes: [NSArray arrayWithObjects: MyDragType, NSFilenamesPboardType, nil]];
	[outlineView registerForDraggedTypes: [NSArray arrayWithObjects: NodePboardType, nil]];
	
	[outlineView setTarget:self]; 
	[outlineView setDoubleAction:@selector(editChild:)];
	
	[keystroke setCanCaptureGlobalHotKeys:YES];
	if([[applescriptController script] source] != nil) [[applescriptController scriptView] setSource:[[applescriptController script] source]];
	[notificationCenter addObserver:self selector:@selector(syncOSAObjects:) name:NSTextDidChangeNotification object:[applescriptController scriptView]];
	
	[notificationCenter addObserver:self selector:@selector(selectAppAndSetMainView) name:NSTableViewSelectionDidChangeNotification object:appTable];
	
	[self setPrefs];
	
	if([[NSNumber numberWithInt:[appTable selectedRow]] isEqualToNumber:[NSNumber numberWithInt:-1]])
	 {
		 [mainView selectTabViewItemWithIdentifier:@"aboutTab"];
	 }
	else{
		[mainView selectTabViewItemWithIdentifier:@"propertiesTab"];
		[rootNode release];
		rootNode = [appArray objectAtIndex:[appTable selectedRow]];
		if(!isUpdating)
		{
			[outlineView reloadData];
		}
	}
	
	[self updateAppTable];
	[self savePrefs];
}

-(void)updateAppTable
{
	[appTable reloadData];
	NSLog(@"the new origin is x=%d, y=%d", [sidebarTab frame].origin.x,[sidebarTab frame].origin.y);
	NSLog(@"the new size is width=%d, height=%d", [sidebarTab frame].size.width,[sidebarTab frame].size.height);
	if([[NSNumber numberWithInt:[appArray count]] isEqualToNumber:[NSNumber numberWithInt:0]])
	{
		[sidebarTab selectTabViewItemWithIdentifier:@"unpopulatedSourceTab"];
		[mainView selectTabViewItemWithIdentifier:@"aboutTab"];
	}
	else {
		[sidebarTab selectTabViewItemWithIdentifier:@"populatedSourceTab"];
	}
}
	 
-(void)syncOSAObjects:(NSNotification *)note
{
	[applescriptController setScript:[[OSAScript alloc] initWithSource:[[applescriptController scriptView] source]]];
}
	 
//- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
-(void)addApp:(NSString *)filename withDefaults:(NSDictionary*)dict
{
	ApplicationMenu * newAppMenu = [ApplicationMenu alloc];
	if([[filename pathExtension] isEqualTo:@"plist"])
	{
		[newAppMenu initWithDictionary:[NSDictionary dictionaryWithContentsOfFile:filename]];
	}
	else
	{
		[newAppMenu initWithURL:filename withDefaults:dict];
	}
	
	if(![appOrder indexOfObject:[newAppMenu bundleID]] || [appOrder indexOfObject:[newAppMenu bundleID]] == NSNotFound)
	 {
		 [appArray addObject:newAppMenu];
		 [appOrder addObject:[newAppMenu bundleID]];
		 //[newAppMenu saveAppToPlist];
		 [newAppMenu release];
		 [[appArray lastObject] saveAppTo:nil extension:nil];
		 [self updateAppTable];
		 [self savePrefs];
		 [appTable selectRow:[appArray count]-1 byExtendingSelection:NO];
		 [self savePlist];
	 }
	else
	 {
		 NSAlert *addAlert = [[[NSAlert alloc] init] autorelease];
		 [addAlert addButtonWithTitle:@"OK"];
		 [addAlert setMessageText:@"Application already added"];
		 [addAlert setInformativeText:@"Applications cannot be added more than once."];
		 [addAlert setAlertStyle:NSWarningAlertStyle];
		 [addAlert beginSheetModalForWindow:[NSApp mainWindow] modalDelegate:self didEndSelector:NULL contextInfo:nil];
	 }
}
-(void)importAppFromPath:(NSString*)path
{
	[self addApp:path withDefaults:nil];
}
	
-(IBAction)openAddAppPanel:(id)sender
{
	NSOpenPanel *addAppOpenPanel = [NSOpenPanel openPanel];
	[addAppOpenPanel setCanChooseFiles:YES];
	[addAppOpenPanel setCanChooseDirectories:NO];
	[addAppOpenPanel setAllowsMultipleSelection:NO];
	[addAppOpenPanel setAccessoryView:addAppExtraView];
	[addAppOpenPanel beginSheetForDirectory:@"/Applications/"
									   file:nil
									  types:[NSArray arrayWithObjects:@"app",nil]
							 modalForWindow:[NSApp mainWindow]
							  modalDelegate:self
							 didEndSelector:@selector(openPanelDidEnd:
													  returnCode:
													  contextInfo:)
							    contextInfo:nil];
}
-(void)openPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
	if(returnCode == NSOKButton)
	{
		NSString * appPath = [[panel filename] stringByExpandingTildeInPath];
		[panel orderOut:nil];
		[NSApp endSheet:panel];
		[self addApp:appPath withDefaults:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:[addDefaultMenuItemsButton state]], @"Default Items", [NSNumber numberWithInt:[hideDockIconButtonInAdd state]], @"Dock Icon", nil]];
	}
}
-(IBAction)removeApp:(id)sender
{
	if([[NSNumber numberWithInt:[appTable selectedRow]] isEqualToNumber:[NSNumber numberWithInt:-1]])
	{
		NSBeep();
		return;
	}
	
	NSAlert *removePlistAlert = [[[NSAlert alloc] init] autorelease];
	// add setShowsSuppressionButton: (needs defaults)
	[removePlistAlert addButtonWithTitle:@"Delete Application"];
	[removePlistAlert addButtonWithTitle:@"Cancel"];
	[removePlistAlert setMessageText:@"Really delete application from App Hider?"];
	[removePlistAlert setInformativeText:@"If the icon was hidden from the dock, it will be restored to visible.\nMenu configuration will be deleted.\nThis action cannot be undone"];
	[removePlistAlert setAlertStyle:NSWarningAlertStyle];
	[removePlistAlert beginSheetModalForWindow:[NSApp mainWindow] modalDelegate:self didEndSelector:@selector(removeAppAlertDidEnd:returnCode:contextInfo:) contextInfo:nil];
}

-(void)removeAppAlertDidEnd:(NSWindow *)window returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
	NSFileManager * fm = [NSFileManager defaultManager];
	if(returnCode==NSAlertFirstButtonReturn)
	{
		NSError * removeError = [[NSError alloc] init];
		[removeError retain];
		if([rootNode isIconHidden]) [rootNode toggleIconVisibility];
		NSString * pathToDelete = [NSString stringWithFormat:@"%@/%@.plist", [supportFolder supportFolder], [[appArray objectAtIndex:[appTable selectedRow]] bundleID], nil];
		[pathToDelete retain];
		if([fm fileExistsAtPath:pathToDelete])
		{
			if(![fm removeItemAtPath:pathToDelete error:&removeError])
			{
				NSLog(@"deletion error");
			}
			[self relaunchDeamon];
		}
		[removeError release];
		[pathToDelete release];
		[appArray removeObjectAtIndex:[appTable selectedRow]];
		[appOrder removeObjectAtIndex:[appTable selectedRow]];
	}
	if(returnCode == NSAlertSecondButtonReturn)
	{
		//don't do shit
	}
	[fm release];
	[self updateAppTable];
	[self savePrefs];
}

-(void)readApplicationPlists
{
	//read support folder
	//enumerate thru names
	BOOL isDirectory;
	NSFileManager * fm = [NSFileManager defaultManager];
	NSEnumerator * plistEnumerator;
	NSString * currentPlist;
	
	NSString * directory = [supportFolder supportFolder];
	
	if ([fm fileExistsAtPath: directory isDirectory: &isDirectory] && isDirectory)
	{
		plistEnumerator = [appOrder objectEnumerator];
		NSDictionary * contents;
		while ((currentPlist = [plistEnumerator nextObject]))
		{
			NSString * path = [NSString stringWithFormat:@"%@/%@.plist", directory, currentPlist, nil];
			if([fm fileExistsAtPath:path isDirectory:NO])
			{
				contents = [[NSDictionary alloc] initWithContentsOfFile:path];
				ApplicationMenu * theApp = [[ApplicationMenu alloc] initWithDictionary:contents];
				[appArray addObject:theApp];
				[theApp release];
				[contents release];
				[self updateAppTable];
				//[self savePrefs];
			}
		}
	}
}
- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
{
	NSLog(@"origin x=%d, y=%d", [optionsPanel frame].origin.x,[optionsPanel frame].origin.y);
	NSSize windowSize = NSMakeSize(469, 189);
	NSSize tabSize = NSMakeSize(443, 143);
	if([[tabViewItem label] isEqualToString:@"Applescript"])
	{
		windowSize.height = 378;
		tabSize.height = 332;
	}
	//[optionsPanel frame].size = windowSize;
	float difference = [optionsPanel frame].size.height - windowSize.height;// * [optionsPanel userSpaceScaleFactor]
	//windowRect.origin.y -= difference;
	[optionsPanel setFrame:NSMakeRect([optionsPanel frame].origin.x, [optionsPanel frame].origin.y+difference, windowSize.width, windowSize.height) display:YES animate:YES];
	float tabDiff = [tabView frame].size.height - tabSize.height;
	[tabView setFrame:NSMakeRect([tabView frame].origin.x, [tabView frame].origin.y+tabDiff, tabSize.width, tabSize.height)];
}

// an action method
- (IBAction)addChild:(id)sender
{
	editNode = NO;
	[keystroke clearKeyCombo];
	
    [NSApp beginSheet:optionsPanel 
	   modalForWindow:[NSApp mainWindow]
		modalDelegate:self 
	   didEndSelector:NULL
		  contextInfo:nil];
}	

- (IBAction)editChild:(id)sender
{
	if([outlineView selectedRow] > -1)
	{
		editNode = YES;
		
		
		//get selected item and set values
		Node * itemToEdit = [outlineView itemAtRow:[outlineView selectedRow]];
		NSString * itemAction = [itemToEdit itemAction];
		
		if([itemAction isEqualToString:@"Keystroke"])
		{
			[actionsTabView selectTabViewItemAtIndex:0];
			[keystrokeName setStringValue:[itemToEdit itemName]];
			NSLog(@"retunr item rpefs = %@", [[itemToEdit returnItemPreferences] objectForKey:@"modifier"]);
			KeyCombo thekeycombo = AHKeyComboForCocoaModifierFlagsAndKeyCode([[itemToEdit returnItemPreferences] objectForKey:@"modifier"], [itemToEdit itemValue]);
			NSLog(@"flag = %u", thekeycombo.flags);
			[keystroke setKeyCombo:thekeycombo]; 
			//[keystrokeKey setStringValue:[itemToEdit itemValue]];
			//[keystrokeModifier selectItemWithTitle:[[itemToEdit returnItemPreferences] objectForKey:@"modifier"]];
		}
		if([itemAction isEqualToString:@"Applescript"])
		{
			[actionsTabView selectTabViewItemAtIndex:1];
			[applescriptName setStringValue:[itemToEdit itemName]];
			NSLog(@"script to add: %@", [itemToEdit itemValue]);
			[applescriptController setScript:[[OSAScript alloc] initWithSource:[itemToEdit itemValue]]];
			[[applescriptController scriptView] setSource:[[applescriptController script] source]];
			NSLog(@"added: %@", [[applescriptController script] source]);
		}
		if([itemAction isEqualToString:@"Separator"])
		{
			[actionsTabView selectTabViewItemAtIndex:2];
			//Nothing
		}
		if([itemAction isEqualToString:@"Submenu"])//Submenu
		{
			[actionsTabView selectTabViewItemAtIndex:3];
			[submenuName setStringValue:[itemToEdit itemName]];
		}
		
		[NSApp beginSheet:optionsPanel 
		   modalForWindow:[NSApp mainWindow]
			modalDelegate:self 
		   didEndSelector:NULL
			  contextInfo:nil];
	}
	else{
		NSBeep();
	}
}

- (IBAction)addEditAppItem:(id)sender
{
	//[optionsPanel orderOut:nil];
	//[NSApp endSheet:optionsPanel];
	if([[sender title] isNotEqualTo:@"Cancel"])
	{
		
		NSString * itemName;
		NSString * itemAction;
		NSString * itemValue;
		NSMutableDictionary * itemPreferences;
		
		NSTabViewItem * tabViewItem =[actionsTabView selectedTabViewItem];
		
		if([tabViewItem isEqualTo:[actionsTabView tabViewItemAtIndex:0]])//Keystroke
		{
			itemName = [keystrokeName stringValue];
			[keystrokeName setStringValue:@""];
			itemAction = @"Keystroke";
			itemValue = SRStringForKeyCode([keystroke keyCombo].code);//[keystrokeKey stringValue];
			itemPreferences = [[NSMutableDictionary dictionaryWithObject:AHStringForCocoaModifierFlags([keystroke keyCombo].flags) forKey:@"modifier"] retain];
			NSLog(@"new item mods = %@", [itemPreferences objectForKey:@"modifier"]);
			NSLog(@"flags = %u", [keystroke keyCombo].flags);
			[keystroke clearKeyCombo];
		}
		if([tabViewItem isEqualTo:[actionsTabView tabViewItemAtIndex:1]])//Applescript
		{
			itemName = [applescriptName stringValue];
			[applescriptName setStringValue:@""];
			itemAction = @"Applescript";
			NSLog(@"scipt to save: %@", [[applescriptController script] source]);
			[applescriptController setScript:[[OSAScript alloc] initWithSource:[[applescriptController scriptView] source]]];
			itemValue = [[applescriptController script] source];//[NSString stringWithContentsOfFile:[applescriptPath stringValue]];
			NSLog(@"saved: %@", itemValue);
			[applescriptController setScript:[[OSAScript alloc] initWithSource:@""]];
			[[applescriptController scriptView] setSource:@""];
			itemPreferences = [[NSMutableDictionary alloc] init];
		}
		if([tabViewItem isEqualTo:[actionsTabView tabViewItemAtIndex:2]])//Separator
		{
			itemName = @"-------------------";
			itemAction = @"Seperator";
			itemValue = @"";
			itemPreferences = [[NSMutableDictionary alloc] init];
		}
		if([tabViewItem isEqualTo:[actionsTabView tabViewItemAtIndex:3]])//Submenu
		{
			itemName = [submenuName stringValue];
			[submenuName setStringValue:@""];
			itemAction = @"Submenu";
			itemValue = @"";
			itemPreferences = [[NSMutableDictionary alloc] init];
		}
		
		[itemName retain];
		[itemAction retain];
		[itemValue retain];
		[itemPreferences retain];
		
		Node *newNode = [[[Node alloc] initWithName:itemName Action:itemAction Value:itemValue Preferences:itemPreferences Parent:[rootNode bundleID]] retain];
		
		if(!editNode)
		{
			if([outlineView selectedRow] < 0)
			{
				[rootNode addAppItem:newNode];
				if([[newNode itemAction] isEqualToString:@"Submenu"]) 
				{
					[outlineView expandItem:newNode];//Doesn't work... do we need it?
				}
			}
			else
			{
				if([[[outlineView itemAtRow:[outlineView selectedRow]] itemAction] isEqualToString:@"Submenu"])
				{
					[[outlineView itemAtRow:[outlineView selectedRow]] addChildNode:newNode];
					[outlineView expandItem:[outlineView itemAtRow:[outlineView selectedRow]]];
				}
				else if([[[outlineView parentForItem:[outlineView itemAtRow:[outlineView selectedRow]]] itemAction] isEqualToString:@"Submenu"])
				{
					[[outlineView parentForItem:[outlineView itemAtRow:[outlineView selectedRow]]] addChildNode:newNode];
				}
				else
				{
					[rootNode addAppItem:newNode];
				}
			}
		}
		else{
			if([outlineView levelForRow:[outlineView selectedRow]] > 0)
			{
				[[outlineView parentForItem:[outlineView itemAtRow:[outlineView selectedRow]]] replaceChild:[outlineView itemAtRow:[outlineView selectedRow]] withChild:newNode];
			}
			else
			{
				[rootNode replaceAppItemAtIndex:[outlineView selectedRow] withAppItem:newNode];
			}
		}
		[newNode release];
		[itemName release];
		[itemAction release];
		[itemValue release];
		[itemPreferences release];
		[keystroke clearKeyCombo];
		[self savePlist];
	}
	[optionsPanel orderOut:nil];
	[NSApp endSheet:optionsPanel];
		// Brute force reload to update sums
    [outlineView reloadData];
}

- (IBAction)removeChild:(id)sender
{
	NSInteger level = [outlineView levelForRow:[outlineView selectedRow]];
	if(level > 0)
	{
		[[outlineView parentForItem:[outlineView itemAtRow:[outlineView selectedRow]]] removeChild:[outlineView itemAtRow:[outlineView selectedRow]]];
	}
	else if(level == 0)
	{
		[rootNode removeAppItem:[outlineView itemAtRow:[outlineView selectedRow]]];
	}
	else
	{
		NSBeep();
	}
	[outlineView reloadData];
}

- (BOOL)shortcutRecorder:(SRRecorderControl *)aRecorder isKeyCode:(signed short)keyCode andFlagsTaken:(unsigned int)flags reason:(NSString **)aReason
{
	return NO;//accept everything
}

- (IBAction)recordScript:(id)sender
{
	[applescriptController recordScript:applescriptRecordButton];
	[applescriptRecordButton setImage:[[NSImage alloc] initWithContentsOfFile:[[[NSBundle bundleWithIdentifier:@"com.mettamedia.AppHider"] resourcePath] stringByAppendingPathComponent:@"SEStopScriptImage.tiff"]]];
	//[applescriptRecordButton setTitle:@"Stop Rec"];
	[applescriptRecordButton setTarget:self];
	[applescriptRecordButton setAction:@selector(stopRecordScript:)];
}

- (IBAction)stopRecordScript:(id)sender
{
	[applescriptController stopScript:nil];
	[applescriptRecordButton setImage:[[NSImage alloc] initWithContentsOfFile:[[[NSBundle bundleWithIdentifier:@"com.mettamedia.AppHider"] resourcePath] stringByAppendingPathComponent:@"SERecordScriptImage.tiff"]]];
	//[applescriptRecordButton setTitle:@"Record"];
	[applescriptRecordButton setTarget:self];
	[applescriptRecordButton setAction:@selector(recordScript:)];
}

-(IBAction)importScript:(id)sender
{
	NSOpenPanel * importPanel = [NSOpenPanel openPanel];
	NSDictionary * importErrorDict = nil;
	if([importPanel runModalForDirectory:[@"~/Documents/" stringByExpandingTildeInPath] file:nil] == NSOKButton)
	{
		[applescriptController setScript:[[OSAScript alloc] initWithContentsOfURL:[NSURL fileURLWithPath:[importPanel filename]] error:&importErrorDict]];
		if(importErrorDict != nil)
			NSRunAlertPanel(@"Error", @"Error importing applescript", @"OK", nil, nil);
	}
}
			

-(IBAction)changeIcon:(id)sender
{
	if([sender image] == NULL || sender == nil)
	{
		[rootNode removeIcon];
		[appIcon setImage:[rootNode icon]];
		[[[appIcon menu] itemWithTitle:@"Reset icon"] setAction:nil];
		[appTable reloadData];
	}
	else
	{
		[rootNode setIcon:[sender image]];
		id item = [[appIcon menu] itemWithTitle:@"Reset icon"];
		[item setTarget:self];
		[item setAction:@selector(changeIcon:)];
	}
	[self savePlist];
	[self relaunchDeamon];
}

// Data source methods get called automatically

// This method is called repeatedly when the table view is displaying it self. 
- (id)outlineView:(NSOutlineView *)ov child:(int)index ofItem:(id)item
{
    // is the parent non-nil?
    if (item)
        // Return the child
        return [item childAtIndex:index];
    else 
        // Else return the root
        return [rootNode appItemAtIndex:index];
}

// Called repeatedly to find out if there should be an
// "expand triangle" next to the label
- (BOOL)outlineView:(NSOutlineView *)ov isItemExpandable:(id)item
{
    // Returns YES if the node has children
	if([item isKindOfClass:[Node class]])
	{
	   return [item expandable];
	}
	return NO; 
}

// Called repeatedly when the table view is displaying itself
- (int)outlineView:(NSOutlineView *)ov numberOfChildrenOfItem:(id)item
{
    if (item == nil) {
        // The root object;
        return [rootNode appItemsCount];
    }
    return [item childrenCount];
}

// This method gets called repeatedly when the outline view is trying
// to display it self.

- (id)outlineView:(NSOutlineView *)ov 
    objectValueForTableColumn:(NSTableColumn *)tableColumn 
    byItem:(id)item
{
    if([item isKindOfClass:[Node class]])
	{
		// I set the identifier of the columns in IB's inspector
		NSString *identifier = [tableColumn identifier];
		
		// What is returned depends upon which column it is
		// going to appear.
		if ([identifier isEqual:@"itemAction"]){
        return [item itemAction];
		} else {
        return [item itemName];
		}
	}
	return nil;
}

- (BOOL)outlineView:(NSOutlineView *)ov shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
	if([item isKindOfClass:[Node class]])
	{
		if([[item itemAction] isEqualTo:@"Seperator"])
		{
			return NO;
		}
		else{
			return YES;
		}
	}
	return NO;
}
// This method gets called when the user edits the field.

- (void)outlineView:(NSOutlineView *)ov 	
    setObjectValue:(id)object 
    forTableColumn:(NSTableColumn *)tableColumn 
    byItem:(id)item
{
    // The only editable column in the item name column
    // so I know immediately where to put it.
    // If there were more editable columns I would
    // need an if-statement like in the previous method
    [item setItemName:object];
    
    // Brute force reload to update sums
	[outlineView reloadData];
}

- (BOOL)outlineView:(NSOutlineView *)ov writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard {
        // Provide data for our custom type, and simple NSStrings.
    [pboard declareTypes:[NSArray arrayWithObjects:NodePboardType, nil] owner:self];
	
    // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
    //[pboard setData:[NSData data] forType:NodePboardType];
	[pboard setString:[[NSNumber numberWithInt:[outlineView rowForItem:[items lastObject]]] stringValue] forType:NodePboardType];
	
    return YES;
}

- (NSDragOperation)outlineView:(NSOutlineView *)ov validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)childIndex {
	if([info draggingSource] == outlineView)
	{
		
		if(!item && childIndex != -1) //drop on root  || childIndex == NSOutlineViewDropOnItemIndex
		{
			[outlineView setDropItem:nil dropChildIndex:childIndex];
			return NSDragOperationMove;
		}
		
		if([[item itemAction] isEqualToString:@"Submenu"])
		{
			[outlineView setDropItem:item dropChildIndex:childIndex];
			if(childIndex == -1)
			{
				return NSDragOperationGeneric;
			}
			else
			{
				return NSDragOperationMove;
			}
		}
	}
	
	return NSDragOperationNone;
}

- (BOOL)outlineView:(NSOutlineView *)ov acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)childIndex {
	NSPasteboard * pboard = [info draggingPasteboard];
	int oldRow = [[pboard stringForType:NodePboardType] intValue];
	id oSelectedItem = ([outlineView selectedRow] > -1 ? [[outlineView itemAtRow:[outlineView selectedRow]] retain]:nil);
	isUpdating = YES;
	
	if(!item) //drop on root
	{
		if([outlineView levelForRow:childIndex] < 1)
		{
			id object = [outlineView itemAtRow:oldRow];
			id oldParent = ([outlineView levelForItem:object] > 0 ? [outlineView parentForItem:object] : nil);
			[object retain];
			[oldParent retain];
			
			if(oldParent != nil)
			{
				[oldParent replaceChild:object withChild:[NSNull null]];
				[rootNode insertAppItem:object atIndex:childIndex];//[rootNode addAppItem:object];
				[oldParent removeChild:[NSNull null]];
			}
			else
			{
				[rootNode replaceAppItemAtIndex:oldRow withAppItem:[NSNull null]];
				[rootNode insertAppItem:object atIndex:childIndex];//[rootNode addAppItem:object];
				[rootNode removeAppItem:[NSNull null]];
			}
			
			//[rootNode addAppItem:object];
			//id object = [rootNode appItemAtIndex:oldRow];
			//[object retain];
			//[rootNode replaceAppItemAtIndex:oldRow withAppItem:[NSNull null]];
			//[rootNode insertAppItem:object atIndex:childIndex];
			//[rootNode removeAppItem:[NSNull null]];
			[outlineView reloadData];
			[object release];
			
		}
	}
	
	if([[item itemAction] isEqualToString:@"Submenu"])
	{
		id object = [outlineView itemAtRow:oldRow];
		id oldParent = ([outlineView levelForItem:object] > 0 ? [outlineView parentForItem:object] : nil);
		[object retain];
		[oldParent retain];
			
		if(oldParent != nil)
		{
			[oldParent replaceChild:object withChild:[NSNull null]];
			childIndex==-1?[item addChildNode:object]:[item insertChildNode:object atIndex:childIndex];
			[oldParent removeChild:[NSNull null]];
		}
		else
		{
			[rootNode replaceAppItem:object withAppItem:[NSNull null]];
			childIndex==-1?[item addChildNode:object]:[item insertChildNode:object atIndex:childIndex];//[item addChildNode:object];// atIndex:childIndex];
			[rootNode removeAppItem:[NSNull null]];
		}
		[outlineView reloadData];
		[object release];
		[oldParent release];
	}
	
	if(oSelectedItem != nil)
	{
		[outlineView deselectRow:oldRow];
		[outlineView selectRow:[outlineView rowForItem:oSelectedItem] byExtendingSelection:NO];// ([outlineView levelForItem:oSelectedItem] > 0?[[outlineView parentForItem:oSelectedItem] indexOfChild:oSelectedItem]:[rootNode indexOfAppItem:oSelectedItem]) byExtendingSelection:NO];
		[oSelectedItem release];
	}
	isUpdating = NO;
	[self savePlist];
	return YES;
}

- (int)numberOfRowsInTableView:(NSTableView *)tableView
{
	return [appArray count];
}

- (id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)tableColumn
			row:(int)row
{
    id value = nil;
	
    if ([[tableColumn identifier] isEqualToString:@"appName"])
    {
        value = [[appArray objectAtIndex:row] name];
    }
    else
    {
        value = [[appArray objectAtIndex:row] icon];//[[NSImage alloc] initWithSize:NSMakeSize(32,32)];//
    }
	//[self selectAppAndSetMainView];
    return value; 
}

- (BOOL) tableView: (NSTableView *) view
         writeRows: (NSArray *) rows
	  toPasteboard: (NSPasteboard *) pboard
{
	//id object = [appArray objectAtIndex:[[rows lastObject] intValue]];
	//NSData *data = [NSKeyedArchiver archivedDataWithRootObject: object];
	selectedItem = ([appTable selectedRow] > -1 ? [appArray objectAtIndex:[appTable selectedRow]]:nil);
	[selectedItem retain];
	[pboard declareTypes:[NSArray arrayWithObject:MyDragType] owner:self];
	[pboard setString:[[NSNumber numberWithInt:[[rows lastObject] intValue]] stringValue] forType:MyDragType];
	return YES;
}

//macrumors page: 311870ß
- (NSDragOperation) tableView: (NSTableView *) view
				 validateDrop: (id <NSDraggingInfo>) info
				  proposedRow: (int) row
		proposedDropOperation: (NSTableViewDropOperation) operation
{
	NSPasteboard *pboard = [info draggingPasteboard];
	
	if (row > [appArray count])
		return NSDragOperationNone;
	
	if (nil == [info draggingSource]) // From other application
	{
		if ([[pboard types] containsObject:NSFilenamesPboardType])
		{
			NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
			int i;
			int error = 0;
			for (i=0;i<[filenames count];i++)
			{
				if([[[filenames objectAtIndex:i] pathExtension] isNotEqualTo:@"app"])
				{
					error++;
				}
			}
			if(error>0)
			{ 
				
				return NSDragOperationNone;
			}
			else
			{
				[appTable setDropRow:-1 dropOperation:NSTableViewDropOn];
				return NSDragOperationMove;
			}
		}
	}
	else if (appTable == [info draggingSource]) // From self
	{
		[appTable setDropRow:row dropOperation:NSTableViewDropAbove];
		return NSDragOperationMove;
	}
	else // From other documents 
	{
		[appTable setDropRow: row dropOperation: NSTableViewDropAbove];
		return NSDragOperationCopy;
	}
	return NSDragOperationNone;
}

- (BOOL) tableView: (NSTableView *) view
		acceptDrop: (id <NSDraggingInfo>) info
			   row: (int) row
	 dropOperation: (NSTableViewDropOperation) operation
{
	NSPasteboard *pboard = [info draggingPasteboard];
	
	//if (row > [appArray count])
		//return NO;
	
	if (nil == [info draggingSource]) // From other application
	{
		if ([[pboard types] containsObject:NSFilenamesPboardType])
		{
			NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
			int i;
			for (i=0;i<[filenames count];i++)
			{
				[self addAppFromDragNDrop:[filenames objectAtIndex:i]];
			}
			
		}
		
		return YES;
	}
	else if (appTable == [info draggingSource]) // From self
	{
		
		int oldRow = [[pboard stringForType:MyDragType] intValue];
		//BOOL previousSelection = NO;
		//id selectedItem = nil;
//		if([appTable selectedRow] > -1)
//		{
//			previousSelection = YES;
//			selectedItem = [appArray objectAtIndex:[appTable selectedRow]];
//		}
		id object = [appArray objectAtIndex:oldRow];
		[object retain];
		[appArray replaceObjectAtIndex:oldRow withObject:[NSNull null]];
		[appArray insertObject:object atIndex:row];
		[appArray removeObject:[NSNull null]];
		[appTable reloadData];
		[object release];
		
		if([appTable isRowSelected:oldRow])
		{
			[appTable deselectRow:oldRow];
			if(row<oldRow)
			{
				[appTable selectRow:row byExtendingSelection:NO];
			}
			else
			{
				[appTable selectRow:row-1 byExtendingSelection:NO];
			}
		}
		if(selectedItem != nil)
		{
			[appTable selectRow:[appArray indexOfObject:selectedItem] byExtendingSelection:NO];
		}
		[selectedItem release];
	
		id bID = [appOrder objectAtIndex:oldRow];
		[bID retain];
		[appOrder replaceObjectAtIndex:oldRow withObject:[NSNull null]];
		[appOrder insertObject:bID atIndex:row];
		[appOrder removeObject:[NSNull null]];
		[self savePrefs];
		[bID release];
		[self relaunchDeamon];
		return YES;
	}
	else // From other documents
	{
		//create plist?
		/* id object = [NSUnarchiver unarchiveObjectWithData: data];
				[appArray insertObject: object atIndex: row];
				[appTable reloadData];
				
		 */return NO;//return YES; 
	}
	return NO;
}

- (void)addAppFromDragNDrop:(NSString*)filename
{
	[self addApp:filename withDefaults:nil];
}

- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
{
	[self selectAppAndSetMainView];
}

- (void)selectAppAndSetMainView
{
	if([appTable selectedRow] > -1)
	{
		[mainView selectTabViewItemWithIdentifier:@"propertiesTab"];
		[appIcon setImage:[[appArray objectAtIndex:[appTable selectedRow]] icon]];
		[appTitle setStringValue:[[appArray objectAtIndex:[appTable selectedRow]] name]];
		//if([rootNodeArray description])[rootNodeArray release];
		rootNode = [appArray objectAtIndex:[appTable selectedRow]];
		[iconCheckbox setState:[[NSNumber numberWithBool:[rootNode isIconHidden]] intValue]];
		//advPrefGrayScaleValue = [[NSNumber numberWithBool:[rootNode grayScale]] intValue];
		//advPrefTitleValue = [rootNode itemTitle];
		//advPrefTooltipValue = [rootNode toolTip];
		if(advancedSettingsOn)
		{
			[self toggleAdvancedSettings:nil];
			[self saveAdvancedSettings:nil];
		}
		
		[outlineView reloadData];
	}
	else{
		[mainView selectTabViewItemWithIdentifier:@"aboutTab"];
	}
}

-(NSString*)nameOfSelectedApplication
{
	return [rootNode name];
}

-(IBAction)contextMenu:(id)sender
{
	NSString * bid;
	NSWorkspace * ws = [NSWorkspace sharedWorkspace];
	if([[sender menu] isEqualTo:[appIcon menu]])
	{	
		bid = [rootNode bundleID];
		if([[sender title] isEqualTo:@"Reset icon"]) 
		{
			[self changeIcon:nil];
			return;
		}
	}
	else
	{
		bid = [[appArray objectAtIndex:(int)[appTable rowForCurrentContextualMenu]] bundleID];
	}
	
	NSLog(@"bid = %@, sender = %@", bid, [sender title]);
	
	if([[sender title] isEqualTo:@"Reveal in Finder"])
	{
		[ws selectFile:[ws absolutePathForAppBundleWithIdentifier:bid] inFileViewerRootedAtPath:nil];
		return;
	}
	if([[sender title] isEqualTo:@"Open application"])
		[ws launchAppWithBundleIdentifier:bid options:NSWorkspaceLaunchAsync additionalEventParamDescriptor:nil launchIdentifier:nil];
	
	[ws release];
}

- (IBAction)toggleIconVisibility:(id)sender
{
	if(![rootNode isIconHidden]) {
		NSAlert *toggledAlert = [[[NSAlert alloc] init] autorelease];
		[toggledAlert addButtonWithTitle:@"OK"];
		[toggledAlert addButtonWithTitle:@"Cancel"];
		[toggledAlert setMessageText:@"Toggling icon visibility"];
		[toggledAlert setInformativeText:@"When running, the application icon will be hidden from the dock.  If the AppHider deamon is not running, the application may be inaccessible.\n\nIF THE APP HAS BEEN CODE SIGNED, APPHIDER CAN MODIFY YOUR APPS AND BREAK SAVED PASSWORDS, PARENTAL CONTROLS, FIREWALL RULES AND OTHER CORE LEVEL SERVICES BY BEING ENABLED FOR THIS APP. CONTINUE AT YOUR OWN RISK"];
		[toggledAlert setAlertStyle:NSWarningAlertStyle];
		[toggledAlert beginSheetModalForWindow:[NSApp mainWindow] modalDelegate:self didEndSelector:@selector(iconAlertDidEnd:returnCode:contextInfo:) contextInfo:nil];
	}
	else {
		[rootNode toggleIconVisibility];
		[iconCheckbox setState:[[NSNumber numberWithBool:[rootNode isIconHidden]] intValue]];
	}
}

-(IBAction)toggleAdvancedSettings:(id)sender
{
	[advPrefGrayScale setState:[[NSNumber numberWithBool:[rootNode grayScale]] intValue]];
	[advPrefTitle setStringValue:[rootNode itemTitle]];
	[advPrefTooltip setStringValue:[rootNode toolTip]];
	
	// set hidden
	[advPrefGrayScale setHidden:advancedSettingsOn];
	[advPrefTitle setHidden:advancedSettingsOn];
	[advPrefTooltip setHidden:advancedSettingsOn];
	
    advancedSettingsOn = !advancedSettingsOn;
}

-(IBAction)saveAdvancedSettings:(id)sender
{
	if(advancedSettingsOn &&  
	 (([[rootNode itemTitle] isNotEqualTo:[advPrefTitle stringValue]]) 
		      || ([[rootNode toolTip] isNotEqualTo:[advPrefTooltip stringValue]])
			      || ([[NSNumber numberWithBool:[rootNode grayScale]] intValue] != [advPrefGrayScale state])))
		/*	      
			   || ([[rootNode itemTitle] isNotEqualTo:@""] && [[advPrefTitle stringValue] isEqualTo:@""]) 
			   || ([[rootNode toolTip] isNotEqualTo:@""] && [[advPrefTooltip stringValue] isEqualTo:@""])
			      
			   || ([[rootNode itemTitle] isEqualTo:@""] && [[advPrefTitle stringValue] isNotEqualTo:@""])
			   || ([[rootNode toolTip] isEqualTo:@""] && [[advPrefTooltip stringValue] isNotEqualTo:@""])
	    */	   
	//Fix to make if nto gray scale and not advPref gray scale dont go etc... 
	{
		[rootNode setGreyScale:[[NSNumber numberWithInt:[advPrefGrayScale state]] boolValue]];
		[rootNode setItemTitle:[advPrefTitle stringValue]];
		[rootNode setTooltip:[advPrefTooltip stringValue]];
		[self savePlist];
		[self performSelector:@selector(relaunchDeamon) withObject:nil afterDelay:.2];
	}
}

-(void)iconAlertDidEnd:(NSWindow *)window returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
	if(returnCode==NSAlertFirstButtonReturn)
	 {
		 [rootNode toggleIconVisibility];
	 }
	[iconCheckbox setState:[[NSNumber numberWithBool:[rootNode isIconHidden]] intValue]];
}

- (void)savePlist
{
	[rootNode retain];
	[rootNode saveAppTo:nil extension:nil];
	[self relaunchDeamon];	
}

-(BOOL)saveAppTo:(NSString*)path extension:(NSString*)ext
{
	[rootNode retain];
	if([rootNode saveAppTo:path extension:ext]) return YES;
	return NO;
}
			
- (void)dealloc
{
	[rootNode saveAppTo:nil extension:nil];
    [rootNode release];
	[self savePrefs];
    [super dealloc];
}

-(IBAction)actOnPref:(id)sender
{
	[self savePrefs];
}

-(IBAction)changeAppDirection:(id)sender
{
	[self savePrefs];
	[self relaunchDeamon];
}

-(void)relaunchDeamon
{
	if([controller performSelector:@selector(isDaemonRunning)])
	{
		[controller performSelector:@selector(terminateDaemon)]; 
		[controller performSelector:@selector(launchDaemon) withObject:nil afterDelay:1]; 
	}
}	

-(void)setPrefs
{
	[appOrder setArray:[AHPrefs getArrayFromKey:CFSTR("Application Order")]];
	[enableOnStartUpButton setState:[[NSNumber numberWithBool:[AHPrefs getBoolFromKey:CFSTR("Enable On Startup")]] intValue]];
    [checkForUpdatesButton setState:[[NSNumber numberWithBool:[AHPrefs getBoolFromKey:CFSTR("Check For Updates")]] intValue]];
	[appDirection selectItemWithTitle:([[AHPrefs getStringFromKey:CFSTR("Application Direction")] isEqualToString:@"RL"]?@"Right to Left":@"Left to Right")];
	[restartDaemonOnChange setState:[[NSNumber numberWithBool:[AHPrefs getBoolFromKey:CFSTR("Restart On Change")]] intValue]];
}

-(void)savePrefs
{
	[AHPrefs setKey:CFSTR("Application Order") fromArray:nil];
	[AHPrefs setKey:CFSTR("Application Order") fromArray:appOrder];
	[AHPrefs setKey:CFSTR("Enable On Startup") fromBool:[[NSNumber numberWithInt:[enableOnStartUpButton state]] boolValue]];
	[AHPrefs setKey:CFSTR("Check For Updates") fromBool:[[NSNumber numberWithInt:[checkForUpdatesButton state]] boolValue]];
	[AHPrefs setKey:CFSTR("Application Direction") fromString:([[appDirection titleOfSelectedItem] isEqualToString:@"Right to Left"]?@"RL":@"LR")];
	[AHPrefs setKey:CFSTR("Restart On Change") fromBool:[[NSNumber numberWithInt:[restartDaemonOnChange state]] boolValue]];
	[AHPrefs savePrefs];
}
@end
